/**File: server.c **/
#include "unpifiplus.h"
#include "unprtt.h"
#include "server.h"
#include "get_ifi_info_plus.c"
#include <stdio.h>

typedef enum {false, true} bool;



int main(int argc, char **argv) {
    printf("\nComputer Science 533: Assignment 2 \n");
    printf("Group 16 - Keerthi Yendamuri, Raman Nanda, Supriya Vasudevan \n");
    printf("Server Application \n\n");
    struct serverArgumentsType serverArgs = serverArgumentProcessing();
    struct socketDiscriptors socketDiscriptors = bindServerAddresses(serverArgs.portNumber);    
    
	printf("Discriptor Count: %d\n", socketDiscriptors.discriptorCount);
	int i;
	for(i = 0; i < socketDiscriptors.discriptorCount; i++) {
		printf("\tIP Address: %s\n", socketDiscriptors.ipAddressBounded[i]);
		printf("\t\tNetwork Mask: %s\n", socketDiscriptors.networkMask[i]);
		printf("\t\t\tSubnet Address: %s\n", socketDiscriptors.subnetAddress[i]);
	}
	printf("\nStarting Monitoring Sockets - Waiting for Client Connection...\n");
	printf("\n");
	monitorSockets(socketDiscriptors);
    exit(0);
}

struct serverArgumentsType serverArgumentProcessing() {
    struct serverArgumentsType serverArgs;
    
    /** Segment For Reading Data From File **/
    FILE *argumentFile;
    char line[MAXLINE];
    int lineCount = 0;
    
    if(!(argumentFile = fopen (SERVER_ARG_FILE, "rt"))) {
        printf("Error: Can not open file: %s", SERVER_ARG_FILE);
        exit(0);
    }
    
    printf("Reading Arguments from File: %s\n", SERVER_ARG_FILE);
    while(fgets(line, MAXLINE, argumentFile) != NULL) {
        lineCount++;
        
        /** To Remove New Line Return from Each Line **/
        size_t bufferlen = strlen(line);
        if (line[bufferlen - 1] == '\n') 
            line[bufferlen - 1] = '\0';
        
        switch(lineCount) {
            case 1:
                serverArgs.portNumber = atoi(line);
                break;
            case 2:
                serverArgs.sendingWindowSize = atoi(line);
                break;
            default:
                printf("Error! Arguments Do NOT Match!");
                exit(0);
        }
    }
    fclose(argumentFile);
    
    printf("\tPort Number: %d\n", serverArgs.portNumber);
    printf("\tMax Sending Sliding-Window Size: %d\n", serverArgs.sendingWindowSize);
    return serverArgs;
}

struct socketDiscriptors bindServerAddresses(int portNumber) {
	struct socketDiscriptors socketDiscriptors;
	
	int sockfd;
	const int on = 1;
	struct ifi_info *ifi, *ifihead;
	struct sockaddr_in *sa;
	
	int socketDiscript[50];
	char *ipAddressBound[50];
	char *networkMask[50];
	char *subnetAddress[50];
	
	int count = 0;
	
	for (ifihead = ifi = Get_ifi_info_plus(AF_INET, 1);
		ifi != NULL; ifi = ifi->ifi_next) {
		
		bool isInterfaceUp = false;
		bool isInterfaceBCast = false;
		bool isInterfaceMCast = false;
		bool isInterfaceLoop = false;
		bool isInterfaceP2P = false;
		
		/** Not Sure what this is **/
		if (ifi->ifi_index != 0)
			printf("(%d) ", ifi->ifi_index);
			
		
		if (ifi->ifi_flags & IFF_UP)			isInterfaceUp = true;
		if (ifi->ifi_flags & IFF_BROADCAST)		isInterfaceBCast = true;
		if (ifi->ifi_flags & IFF_MULTICAST)		isInterfaceMCast = true;
		if (ifi->ifi_flags & IFF_LOOPBACK)		isInterfaceLoop = true;
         	if (ifi->ifi_flags & IFF_POINTOPOINT)	     isInterfaceP2P = true;
		
     	sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
        
     	if(sockfd >= 0) { 
      		Setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

      		sa = (struct sockaddr_in *) ifi->ifi_addr;
      		sa->sin_family = AF_INET;
      		sa->sin_port = htons(portNumber);

      		if(bind(sockfd, (SA *) sa, sizeof(*sa)) != -1) {
      			fprintf(stdout, "Bound %s\n", Sock_ntop((SA *) sa, sizeof(*sa)));
      			socketDiscript[count] = sockfd;
      			ipAddressBound[count] = malloc(sizeof(char[INET_ADDRSTRLEN]) + 1);
      			strncpy(ipAddressBound[count], Sock_ntop_host((SA *) sa, sizeof(*sa)), sizeof(char[INET_ADDRSTRLEN]));
      			if ((sa = (struct sockaddr_in *) ifi->ifi_ntmaddr) != NULL) {
      				networkMask[count] = malloc(sizeof(char[INET_ADDRSTRLEN]) + 1);
      				strncpy(networkMask[count], Sock_ntop_host((SA *) sa, sizeof(*sa)), sizeof(char[INET_ADDRSTRLEN]));
      			}
			
      			struct in_addr inetIPaddr, inetMaskaddr;
      			if(inet_aton(ipAddressBound[count], &inetIPaddr) != 0) {
      				if(inet_aton(ipAddressBound[count], &inetMaskaddr) != 0) {
      					unsigned long subnetAddr = inetIPaddr.s_addr & inetMaskaddr.s_addr;
      					subnetAddress[count] = malloc(sizeof(char[INET_ADDRSTRLEN]) + 1);
      					inet_ntop(AF_INET, &subnetAddr, subnetAddress[count], sizeof(char[INET_ADDRSTRLEN]));
      				}
      			}
      			count++;
      		}		
      	}	
	}
	socketDiscriptors.sockfd = malloc(count*sizeof(int));
	socketDiscriptors.ipAddressBounded = malloc(count * sizeof *socketDiscriptors.ipAddressBounded);
	socketDiscriptors.networkMask = malloc(count * sizeof *socketDiscriptors.networkMask);
	socketDiscriptors.subnetAddress = malloc(count * sizeof *socketDiscriptors.subnetAddress);
	
	int i;
	for (i = 0; i < count; i++) {
     	socketDiscriptors.sockfd[i] = socketDiscript[i];
		socketDiscriptors.ipAddressBounded[i] = ipAddressBound[i];
		socketDiscriptors.networkMask[i] = networkMask[i];
		socketDiscriptors.subnetAddress[i] = subnetAddress[i];
	}
	socketDiscriptors.discriptorCount = count;
    return socketDiscriptors;
}

void sig_chld(int signo) {
    pid_t pid;
    int stat;
    while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
        printf("Child %d Terminated\n", (int)pid);
    return;
}

void receiveMessage(int fd) {
	struct msghdr message;
	//int flags = MSG_DONTROUTE;
  int idx = 0;
	
	// Error check.
	if (recvmsg(fd, &message, MSG_DONTROUTE) == -1) {
		printf("Error receiving message\n");
	}
	
	// There will be message.iovlen number of msg_iov.
	for (idx = 0; idx < message.msg_iovlen; ++idx) {
		struct iovec a_iovec = message.msg_iov[idx];
		printf("Message : %s\n", a_iovec.iov_base);
	}
	
	return;
}


struct hdr {
    uint32_t	seq;	/* sequence # */
    uint32_t	ts;		/* timestamp when sent */
} recvhdr;
static struct msghdr msgrecv; 
static struct rtt_info rttinfo;
static int rttinit = 0;

void monitorSockets(socketDiscriptors socketDiscriptors) {
	struct sockaddr_in cliaddr;
	socklen_t len;
	fd_set rset;	
	int nready;
	ssize_t n;
     	char message[MAXLINE + 1];
	char ackMessage[MAXLINE+1];
	pid_t pid;
	
	FD_ZERO(&rset);
	int maxfd, i;
	maxfd = 0;

	/* Input File Parameters */
	char line[MAXLINE+1];
	int lineCount=0;
	FILE *inputFile;

	
	if(socketDiscriptors.discriptorCount < 1) {
     	err_quit("Error: There are no sockets to watch!");
     }
     else if(socketDiscriptors.discriptorCount == 1) {
        maxfd = socketDiscriptors.sockfd[0];
	}
	else {
     	maxfd = socketDiscriptors.sockfd[0];
     	for(i = 1; i < socketDiscriptors.discriptorCount; i++) {
           	maxfd = max(maxfd, socketDiscriptors.sockfd[i]);
     	}
	}
	
	Signal(SIGCHLD, sig_chld);
     
	
	for( ; ; ) {
     	for(i = 0; i < socketDiscriptors.discriptorCount; i++) {
           	FD_SET(socketDiscriptors.sockfd[i], &rset);
     	}
     	if((nready = select(maxfd + 1, &rset, NULL, NULL, NULL)) < 0) {
           	if(errno == EINTR)
               	continue;
              else
                   err_sys("Select Error!");
     	}
     	for(i = 0; i < socketDiscriptors.discriptorCount; i++) {
           	if(FD_ISSET(socketDiscriptors.sockfd[i], &rset)) {
               		len = sizeof(cliaddr);
                   
                   	if(rttinit == 0) {
                      		rtt_init(&rttinfo);
                      		rttinit = 1;
                      		rtt_d_flag = 1;
                   	}
               	
               		struct iovec iovrecv[2];
               		msgrecv.msg_name = NULL;
              		msgrecv.msg_namelen = 0;
              		msgrecv.msg_iov = iovrecv;
              		msgrecv.msg_iovlen = 2;
              		iovrecv[0].iov_base = &recvhdr;
              		iovrecv[0].iov_len = sizeof(struct hdr);
              		iovrecv[1].iov_base = message;
              		iovrecv[1].iov_len = MAXLINE;

               		//n = Recvmsg(socketDiscriptors.sockfd[i], &msgrecv, 0);
               		n = Recvfrom(socketDiscriptors.sockfd[i], message, MAXLINE, 0, (SA *) &cliaddr, &len);
                	//Sendto(socketDiscriptors.sockfd[i], message, n, 0, (SA *) &cliaddr, &len);
 			printf("Socket Descriptor after recvmsg:%d\n",socketDiscriptors.sockfd[i]); 

                	printf("Recieved %d Byte Datagram from Client: %s\n", n, Sock_ntop((SA *) &cliaddr, len));
			//Sendto(socketDiscriptors.sockfd[i], , n, 0, (SA *) &cliaddr, len);
			//Sendmsg()
			printf("Attempting to Fork to Child Server Process…\n");              
   			printf("Message Rece - File Nameived:%s\n",message); 
				if((pid = Fork()) == 0) {
				
					
					printf("Sucessfully Forked Process, Starting Child…\n"); 
					Connect(socketDiscriptors.sockfd[i],(SA*)&cliaddr, sizeof(cliaddr));
					printf("Client Port:%d\n",cliaddr.sin_port);
					printf("Client address:%s\n",inet_ntoa(cliaddr.sin_addr));	
					Sendto(socketDiscriptors.sockfd[i],message,strlen(message),MSG_DONTROUTE,NULL,NULL);  
					/*printf("Waiting for acknowledgement from client\n");	
				
					fflush(stdin);	
					Recvfrom(socketDiscriptors.sockfd[i],message,strlen(message),MSG_DONTROUTE,NULL,NULL);  
					printf("Acknowledgement from client:%s\n",message);	
					if((strcmp(message,"Ack")) == 0){
						printf("Acknowledgement received from client");
					}
					*/
					/* Reading from the file */
					if(!(inputFile = fopen(message,"rt"))){
						printf("Error: Can not open file: %s", message);
						exit(0);
					}	

	
					while(Fgets(line,MAXLINE,inputFile) != NULL){
				
						Sendto(socketDiscriptors.sockfd[i],line,strlen(line),MSG_DONTROUTE,NULL,NULL);  
						lineCount++;

					}
					printf("Number of lines in the file:%d\n",lineCount);	
					//serverTransfer(socketDiscriptors.sockfd[i],&cliaddr, sizeof(cliaddr), message, recvhdr.seq, (ssize_t *) n);
					exit(0);
				}
				//Sendto(socketDiscriptors.sockfd[i], message, n, 0, (SA *) &cliaddr, len);
           	}
     	}          
	}
	exit(0);
}

void serverTransfer(int sockfd, const struct sockaddr_in *destaddr, socklen_t clilen, char* fileName, uint32_t seq, ssize_t* n)
{
	int nSend;	
	struct sockaddr_in peer;
	int len;

	len = sizeof(destaddr);	
	/* Bullet 6 In Directions */

	printf("Destination port:%d\n",destaddr->sin_port);
	
	/* Need to Check to See if Connection is Local -- Similar to Client*/
	bzero(&destaddr,sizeof(destaddr));
	Connect(sockfd,(SA*)&destaddr, sizeof(destaddr));
	getpeername(sockfd, (SA*)&peer, &len);
	printf("Client address here:%s\n",inet_ntoa(peer.sin_addr));  
	printf("%u\n",(unsigned)ntohs(peer.sin_port)); 

	
	printf("Retrieved File Request for File Name: %s\n", fileName);
	printf("Creating New Socket for Client\n %s \n", Sock_ntop_host(destaddr,sizeof(*destaddr)));
	//int newSockfd = Socket(AF_INET, SOCK_DGRAM, 0);

	struct msghdr msgsend;
	struct hdr {
     	uint32_t seq;
     	uint32_t ts;
	} sendhdr;
	
	struct iovec iovsend[2];
	sendhdr.seq = seq;
	sendhdr.ts = 0;
	iovsend[0].iov_base = &sendhdr;
	iovsend[0].iov_len = sizeof(struct hdr);
	iovsend[1].iov_base = fileName;
	iovsend[1].iov_len = sizeof(fileName);
	printf("This seq num: %d\n", seq);
	//sendhdr.seq = seq;
	memset(&msgsend, 0, sizeof(msgsend));
	msgsend.msg_name = destaddr;
	msgsend.msg_namelen = clilen;
	msgsend.msg_iov = &iovsend;
	msgsend.msg_iovlen = 2;
	
	//msgsend.msg_accrights=0;
	//msgsend.msg_accrightslen=0;

	printf("Attempting to Send Message Back as Seq Number %d…\n", sendhdr.seq);
	//Sendto(sockfd, fileName, n, 0, pcliaddr, clilen);
	//ssize_t nsent = sendmsg(sockfd, &msgsend, 0);
/*	if ((nSend = sendmsg(sockfd,&msgsend,MSG_DONTROUTE))==-1) {
        printf("%s\n",strerror(errno));
     } else {
        printf("Sucessfully Sent Message:!\n");
     }
 */    
 	printf("Socket Descriptor before sendmsg:%d\n",sockfd); 
	Sendto(sockfd,fileName,strlen(fileName),MSG_DONTROUTE,NULL,NULL);  
	
	
	//printf("Sent Message! %d - %d", (int) nsent, errno);
	//Sendto(sockfd, fileName, (int)recvSize, 0, (SA *) &pcliaddr, clilen);
	
	
	//const int on = 1;
	//if(sockfd >= 0) { 
    // 	Setsockopt(newSockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
     	
     	
    // 	if(bind(newSockfd, (SA *) pcliaddr, sizeof(*pcliaddr)) != -1) {
	//		fprintf(stdout, "Bound %s\n", Sock_ntop((SA *) pcliaddr, sizeof(*pcliaddr)));
	//		//Sendto(sockfd, "Test", sizeof("Test"), 0, (SA *) &cliaddr, len);
    // 	}
     //}
//	exit(0);
}

